/*******************************************************************************

ATTENTION, this is an altered version of the xhtml_gen library, specifically tailored to libwebconf's needs please refer to the original library

xhtml_gen: xhtml generation library.
Copyright (c) 2006  Mitchel Haas

This software is provided 'as-is', without any express or implied warranty. 
In no event will the author be held liable for any damages arising from 
the use of this software.

Permission is granted to anyone to use this software for any purpose, 
including commercial applications, and to alter it and redistribute it freely, 
subject to the following restrictions:

1.	The origin of this software must not be misrepresented; 
you must not claim that you wrote the original software. 
If you use this software in a product, an acknowledgment in the product 
documentation would be appreciated but is not required.

2.	Altered source versions must be plainly marked as such, 
and must not be misrepresented as being the original software.

3.	The above copyright notice and this permission notice may not be removed 
or altered from any source distribution.

For complete documentation on this library, see http://www.datasoftsolutions.net
Email questions, comments or suggestions to mhaas@datasoftsolutions.net
*******************************************************************************/
#include "xhtml_element.hpp"

namespace xhtml_gen
{

inline content* strict_clone_fcn(const content& content_obj)
{
	return content_obj.clone();
}


// default constructor
xhtml_element::xhtml_element() : basic_tree_type(content()) 
{ 
	set_clone_function();
}


xhtml_element::xhtml_element(const content& _content) 
{ 
	set_clone_function();
	set(_content); 
}

// constructor for strict type element
xhtml_element::xhtml_element(xhtml_strict _tag, const std::string& _id, const std::string& _class) 
{  
	set_clone_function();
	set(tag(_tag)); 

	if (!_id.empty()) {
		set_attribute(id, _id);
	}

	if (!_class.empty()) {
		set_attribute(class_attr, _class);
	}
}


// copy constructor
xhtml_element::xhtml_element(const xhtml_element& _src) : basic_tree_type(_src)
{
	set_clone_function();
	 basic_tree_type::const_iterator it = _src.begin();
	const  basic_tree_type::const_iterator it_end = _src.end();
	for ( ; it != it_end; ++it ) {  // do a deep copy by inserting children (and descendants)
		insert(*it);
	}
}

// assignment operator
xhtml_element& xhtml_element::operator = (const xhtml_element& rhs)
{
	if ( this == &rhs ) // check for self assignment
		return *this;

	basic_tree_type::clear();
	basic_tree_type::operator =(rhs); // call base class operation

	 basic_tree_type::const_iterator it = rhs.begin();
	const  basic_tree_type::const_iterator it_end = rhs.end();
	for ( ; it != it_end; ++it ) // insert children and descendants
	{
		insert(*it);
	}
	return *this;
}

// operator []
xhtml_element& xhtml_element::operator [](const int _index) 
{ 
	if (_index < 0 || _index >= basic_tree_type::size())
		throw std::out_of_range("sequential_tree index out of range");

	 xhtml_element::iterator it = basic_tree_type::begin(); 
	for (int i = 0; i < _index; ++i) 
		++it; 
	
	return *it; 
}

// operator [] const
const xhtml_element& xhtml_element::operator [](const int _index) const 
{ 
	if (_index < 0 || _index >= basic_tree_type::size())
		throw std::out_of_range("sequential_tree index out of range");

	 xhtml_element::const_iterator it = basic_tree_type::begin(); 
	for (int i = 0; i < _index; ++i) 
		++it; 
	
	return *it; 
}


std::string xhtml_element::get_xhtml(const int indent) const
{
	std::ostringstream ostr;
	const tag* const pTag = get()->get_tag();
	nesting_type last_tag_type = get_nesting_type();

	if (pTag)
		last_tag_type = pTag->get_nesting_type();

	ostr << get()->get_xhtml(indent);//context;
	
	 xhtml_element::const_iterator it = basic_tree_type::begin();
	const  xhtml_element::const_iterator it_end = basic_tree_type::end();
	for (; it != it_end; ++it) {
		const tag* pChild_tag = it->get()->get_tag();
		// if the last xhtml_element wasn't inline
		if (pChild_tag) {
			if (last_tag_type != in_line && last_tag_type != cdata ) {
				if (pChild_tag->get_nesting_type() == in_line || pChild_tag->get_nesting_type() == cdata ) {
					ostr << std::string((indent + 1) * 3, ' ');
				}
				last_tag_type = pChild_tag->get_nesting_type();
			} else {
				// last tag type was inline
				if (pChild_tag && pChild_tag->get_nesting_type() != in_line) {
					ostr << "\n";  
				}
			}
		} else {
			if (last_tag_type != in_line && last_tag_type != cdata && last_tag_type != xhtml_gen::empty)
				ostr << std::string((indent + 1) * 3, ' ');
			last_tag_type = cdata;
		}
		ostr << it->get_xhtml(indent + 1);
	}

	if (!basic_tree_type::empty() && (last_tag_type == in_line || last_tag_type == cdata) && pTag && pTag->get_nesting_type() != in_line) {
		ostr << "\n";
	}

	if (pTag) {
		if (pTag->get_nesting_type() != xhtml_gen::empty)
			ostr << pTag->get_end_tag(indent);
	}

	return ostr.str();
}

bool xhtml_element::set_attribute( const attribute _attribute, const std::string& _value ) 
{ 
	tag* const pTag = get()->get_tag();
	if ( pTag ) {
		return pTag->set_attribute(_attribute, _value);
	}

	return false;
}

bool xhtml_element::set_style( const css_property _property, const std::string& _value ) 
{
	tag* const pTag = get()->get_tag();
	if (pTag) {
		return pTag->set_style(_property, _value);
	}

	return false;
}

std::string xhtml_element::get_attribute( const attribute _attribute ) const
{
	const tag* const pTag = get()->get_tag();
	if (pTag) {
		return pTag->get_attribute(_attribute);
	}
	return "";
}

std::string xhtml_element::get_style( const css_property _property ) const
{
	const tag* const pTag = get()->get_tag();
	if (pTag) {
		return pTag->get_style(_property);
	}
	return "";
}

nesting_type xhtml_element::get_nesting_type() const 
{ 
	const tag* pTag = get()->get_tag(); 
	if (pTag) 
		return pTag->get_nesting_type(); 
	
	return cdata; 
}

xhtml_element& xhtml_element::operator << (const std::string& _pc_data)
{
	const tag* const pTag = get()->get_tag();
	if ( pTag && pTag->allow_cdata() ) {
		pc_data data(_pc_data);
		basic_tree_type::insert(data, this);
	}
	return *this;

}

xhtml_element& xhtml_element::operator << (const xhtml_element& elem)
{
	const tag* const pTag = get()->get_tag();

	if ( pTag ) {
		const tag* const pChild_tag = elem.get()->get_tag();
		if ( pChild_tag ) {
			if ( pTag->validate_nesting(pChild_tag) ) {
				basic_tree_type::insert(elem, this);
			}
		} else {
			if ( pTag->allow_cdata() ) {
				pc_data data(elem.get()->get_xhtml(0));
				basic_tree_type::insert(data, this);
			}
		}
	}
	return *this;
}

 xhtml_element::iterator xhtml_element::insert(const std::string& _pc_data)
{
	const tag* const pTag = get()->get_tag();
	if ( pTag && pTag->allow_cdata() ) {
		pc_data data(_pc_data);
		return insert(data);
	}

	return basic_tree_type::end();
}

 xhtml_element::iterator xhtml_element::insert(const xhtml_element& elem)
{
	const tag* const pTag = get()->get_tag();

	if ( pTag ) {
		const tag* const pChild_tag = elem.get()->get_tag();
		if ( pChild_tag ) {
			if ( pTag->validate_nesting(pChild_tag) ) {
				return basic_tree_type::insert(elem, this);
			}
		} else {
			if ( pTag->allow_cdata() ) {
				xhtml_gen::pc_data data(elem.get()->get_xhtml(0));
				return basic_tree_type::insert(data, this);
			}
		}
	}
	return basic_tree_type::end();
}

 xhtml_element::iterator xhtml_element::insert( xhtml_element::const_iterator pos, const std::string& _pc_data)
{
	const tag* const pTag = get()->get_tag();
	if ( pTag && pTag->allow_cdata() ) {
		pc_data data(_pc_data);
		return insert(pos, data);
	}

	return basic_tree_type::end();
}

 xhtml_element::iterator xhtml_element::insert( xhtml_element::const_iterator pos, const xhtml_element& elem)
{
	// insert current node
	 xhtml_element::iterator base_it = insert(pos, *elem.get());

	if ( base_it != basic_tree_type::end() ) {
		 xhtml_element::const_iterator it = elem.begin(), it_end = elem.end();

		// call this function recursively thru derived tree for children
		for ( ; it != it_end; ++it )
			base_it->insert(*it);
	}
	return base_it;
}

 xhtml_element::iterator xhtml_element::insert( xhtml_element::const_iterator pos, const content& _content)
{
	// create a new tree_type object to hold the node object
	xhtml_element* pElem = 0; 
	basic_tree_type::allocate_tree_type(pElem, xhtml_element(_content));
	pElem->set_parent(this);

	const  container_type::size_type sz = basic_tree_type::children.size();

	// calculate the insertion point
	const  xhtml_element::const_iterator beg_it = basic_tree_type::begin();
	 container_type::iterator pos_it = basic_tree_type::children.begin(); 
	for ( ; pos != beg_it; --pos, ++pos_it) ;
	// insert the tree node into the children container
	const  container_type::iterator it = basic_tree_type::children.insert(pos_it, pElem);

	if ( sz == basic_tree_type::children.size() ) { // check for successful insertion
		basic_tree_type::deallocate_tree_type(pElem);  // not successful.  delete new node and return end()
		 basic_tree_type::iterator end_it(basic_tree_type::children.end(), this);
		return end_it;
	}

  xhtml_element::iterator node_it(it, this);

	return node_it;
}

inline void xhtml_element::set_clone_function()
{
	basic_tree_type::set_clone(&strict_clone_fcn);
}


}
